package com.loren.demo23.license;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.loren.demo23.utils.AESUtils;
import com.loren.demo23.utils.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StreamUtils;

import java.nio.charset.StandardCharsets;
import java.util.Objects;

@Slf4j
@Component
public class LicenseServiceImpl implements LicenseService {

    private static final String KEY = "1234567890";

    @Override
    public String licenseText(License license) {
        try {
            license.setSign(null);
            String jsonString = JSONObject.toJSONString(license, SerializerFeature.IgnoreNonFieldGetter);
            String md5Encrypt = DigestUtils.md5DigestAsHex(jsonString.getBytes());
            String keyPrivate = StreamUtils.copyToString(new ClassPathResource("key_private").getInputStream(), StandardCharsets.UTF_8);
            // Sign = RSA(MD5(License) + KeyPrivate)
            String sign = RSAUtils.encrypt(md5Encrypt, keyPrivate);
            // Set Sign
            license.setSign(sign);
            // LicenseText = encrypt(License)
            return AESUtils.encrypt(JSONObject.toJSONString(license), KEY);
        } catch (Exception e) {
            log.error("Get Licence Text error", e);
            throw new RuntimeException("Get Licence Text error");
        }
    }

    @Override
    public void validate(String licenseText) {
        try {
            String jsonString = AESUtils.decrypt(licenseText, KEY);
            License license = JSONObject.parseObject(jsonString, License.class);
            String keyPublic = StreamUtils.copyToString(new ClassPathResource("key_public").getInputStream(), StandardCharsets.UTF_8);
            String md5Decrypt = RSAUtils.decrypt(license.getSign(), keyPublic);
            license.setSign(null);
            String md5Encrypt = DigestUtils.md5DigestAsHex(JSONObject.toJSONString(license, SerializerFeature.IgnoreNonFieldGetter).getBytes());
            if (!Objects.equals(md5Decrypt, md5Encrypt)) {
                throw new LicenseException("Illegal license text");
            }
            this.validate(license);
        } catch (Exception e) {
            if (e instanceof LicenseException) {
                throw (LicenseException) e;
            }
            log.error("Validate licence text failed", e);
            throw new RuntimeException("Validate licence text failed");
        }
    }

    private void validate(License license) {
        ServerInfo serverInfo = ServerInfo.getInstance();
        if (!Objects.equals(serverInfo.getHardwareUUID(), license.getHardwareUUID())) {
            throw new RuntimeException("Validate licence text failed, System information mismatch");
        }
    }
}
